import { CENTEREVENTS, CSLine, CSys, MathTool } from '@amcax/base';
import { nextmeshEventCenter } from '@app-cad/environments/nextmesh/common/events/EventCenter';
import Base, { MdEn, PlaneEnum, PlaneType } from '../cmd-common/base';
import { nmAppLog } from '@app-nextmesh/common/utils';
import { State } from './type';
import {
  EllipseCurve,
  Geometry,
  Object3D,
  Vector3,
  Vector3Tuple,
} from 'three-legacy';
import { ctx2 } from '@app-base/module';
import { distanceToLine, getAngle, RETAIN } from '@app-base/common/utils';
import { createUpdateLine } from '../cmd-common/tools';
import { documentV2 } from '@app-nextmesh/domain';

export default class CmdInteractor extends Base {
  state: State = State.NONE;
  planeNormal: Vector3;
  planeOrigin: Vector3;
  faceCSys: CSys;
  startPoint: Vector3;
  secondPoint: Vector3;
  endPoint: Vector3;
  xRadius: number = 0; // 椭圆Xradius
  yRadius: number = 0; // 椭圆Yradius
  angle: number = 0; // 椭圆旋转角度
  xVec: Vector3; // 椭圆原点
  yVec: Vector3; // 椭圆X轴
  assisGroup: Object3D;
  planeType: PlaneType;
  previewData: {
    line: CSLine;
  };
  constructor(planeType: PlaneType = PlaneEnum.XOY) {
    super();
    this.assisGroup = ctx2.cadScene.assisGroup;
    this.setMode(['plane', 'point']);
    this.setPlaneMode(planeType);
    this.registerEvents();
    this.previewData = {
      line: null,
    };
  }
  setPlaneMode = (plane: PlaneEnum) => {
    this.planeType = plane;
    this.getPlane();
  };
  reset(): void {
    this.state = State.NONE;
    this.previewData?.line?.destroy();
    this.previewData.line = null;
  }
  registerEvents = () => {
    nextmeshEventCenter()
      .setState('cmd-mesh-ellipse')
      .on(CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .on(CENTEREVENTS.POINTERDOWN, this.onMouseUp)
      .on(CENTEREVENTS.KEYUP, this.onKeyUp)
      .addExit(this.destroy);
  };
  unregisterEvents = () => {
    nextmeshEventCenter()
      .off(CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .off(CENTEREVENTS.POINTERDOWN, this.onMouseUp)
      .off(CENTEREVENTS.KEYUP, this.onKeyUp)
      .setDefault();
  };
  onMouseMove = (e) => {
    if (this.state === State.NONE) {
      this.mouseMove(e, false);
    } else {
      const mouseXY = this.getMouseWorldVector(e.mouse);
      this.preView([this.startPoint, mouseXY]);
    }
  };
  onMouseUp = (e) => {
    if (!e.button.onLeft) {
      return;
    }
    if (this.state === State.NONE) {
      if (this.pickInfo?.assisObj) {
        const mouseup = this.mouseUp(e);
        this.startPoint = new Vector3(...mouseup.point);
        this.getPlane(
          mouseup.point,
          this.entityType === MdEn.FACE && PlaneEnum.FREE,
        );
      } else {
        this.startPoint = this.getMouseWorldVector(e.mouse);
      }
      this.state = State.DRAW;
    } else if (this.state === State.DRAW) {
      this.secondPoint = this.getMouseWorldVector(e.mouse);
      this.state = State.DRAW_NEXT;
    } else if (this.state === State.DRAW_NEXT) {
      this.endPoint = this.getMouseWorldVector(e.mouse);
      this.finish();
      this.state = State.FINSH;
    }
  };
  async finish() {
    await this.onExecute(false);
    this.close();
  }
  onExecute = async (preview: boolean) => {
    // 旋转轴
    const vx = this.startPoint.clone().sub(this.secondPoint);
    const vec = this.secondPoint.clone().sub(this.endPoint);
    const rotatAxis = new Vector3().crossVectors(vx, vec).normalize();
    const vy = vx
      .clone()
      .applyAxisAngle(rotatAxis, Math.PI / 2)
      .normalize()
      .setLength(this.yRadius);
    let source: Vector3Tuple[] = [
      this.startPoint?.toArray(),
      vx.toArray(),
      vy.toArray(),
    ];
    if (this.xRadius > this.yRadius) {
      source = [this.startPoint?.toArray(), vy.toArray(), vx.toArray()];
    }
    try {
      return await documentV2.basicCurve({ source, mode: 'ellipse' }, preview);
    } catch (error) {
      //
    }
  };
  preView(vertexs: Vector3[]) {
    const p12d = this.change2D(vertexs[0]);
    const p22d = this.change2D(vertexs[1]);
    if (this.state === State.DRAW) {
      this.angle = getAngle(p22d, p12d); // 计算角度
      this.xVec = p12d.clone().sub(p22d);
      this.xRadius = p12d.distanceTo(p22d);
      this.yRadius = MathTool.fixNumber(this.xRadius * 0.45, RETAIN);
    } else if (this.state === State.DRAW_NEXT) {
      const vec = this.xVec;
      const normal = new Vector3(vec.y, -vec.x, 0);
      this.yRadius = distanceToLine<Vector3>(p22d, {
        start: p12d,
        vec,
        normal,
      });
      this.yVec = normal;
    }
    this.previewData.line = createUpdateLine(
      this.getVertices(p12d),
      this.previewData.line,
      this.assisGroup,
    ); //  绘制预览线
    ctx2.viewer.requestRender();
  }
  getVertices(center: Vector3) {
    const ellipseCurve = new EllipseCurve(
      center.x,
      center.y, // centerX, centerY
      this.xRadius,
      this.yRadius, // xRadius, yRadius
      0,
      Math.PI * 2, // startAngle, endAngle
      false, // clockwise
      this.angle, // rotation
    );
    const geometry = new Geometry().setFromPoints(ellipseCurve.getPoints(100));
    return geometry.vertices.map((v) => {
      return this.change3D(v);
    });
  }
  getPlane = (point?: Vector3Tuple, planeType?: PlaneEnum) => {
    const type = planeType || this.planeType;
    const origin = point || this.startPoint?.toArray() || [0, 0, 0];
    this.setPlacement(origin, type);
  };
  onEnter = () => {
    nmAppLog('enter');
  };
  destroy = () => {
    this.reset();
    this.unregisterEvents();
    this.removePointArrow();
    this.assisGroup?.clear();
  };
  close = () => {
    this.destroy();
    this.exitFunc?.();
  };
  addExit(exitFunc: () => void) {
    this.exitFunc = exitFunc;
  }
}
